module Main

import IO;
import String;
import Relation;
import Set;

import lang::java::jdt::Java;
import lang::java::jdt::JDT;
import lang::java::jdt::JavaADT;

import CyclomaticComplexity;
import AdjustedCyclomaticComplexity;
import Regular::StringUtils;
import ModuleSize;
import Constants;

/* 	This module implements the metrics for the Software Evolution
	essay of Arie van der Veek
	
	ASSUMPTION: The project over which the metrics are calculated
	contain onle ONE source code file containing the class that
	contains the recieve() method. If this is not the case
	the metrics may be calculated incorrectly.
	
	example run:
	import Main;
	import Regular::LatexTableFormatter;
	printResultToStringTable(calculateEssayMetrics());
	
*/


// Data type for Java methods
data EssayMetrics = essaymetrics(
		str packageName, 
		str className, 
		int moduleSize,
		int totalCC, 
		real ccPerNLOC, 
		int totalAdjustedCC, 
		real adjustedCCPerNLOC);

list[loc] fileLocations = [ 	
		|project://MainMiscBLIPToFIDS20100921/src/prjMainprjMiscprjBlipTechnicalServicesJCD/jcdFromBlipWTToFIDS.java|,
		|project://CISSCDMFromCISSToCDM20111108/src/prjCISSprjCDMTechnicalServicesJCD/jcdFromCISSToCDM.java|,
		|project://CISSFAVISFromCISSToFAVIS20111024/src/prjCISSprjFAVISTechnicalServicesJCD/jcdFromCISSToFAVIS.java|,
		|project://CISSInternetFromCISSToInternet20111108/src/prjCISSprjInternetTechnicalServicesJCD/jcdFromCISSToInternet.java|,
		|project://CISSKLMFlightsFromCISSToKLMFlights20111108/src/prjCISSprjKLM_CDMTechnicalServicesJCD/jcdFromCISSToKLMFlights.java|,
		|project://CISSKLMFlightsFromKLMFlightsToCISS20111020/src/prjCISSprjKLMFlightsTechnicalServicesJCD/jcdFromKLMFlightsToCISS.java|,
		|project://CISSNOMOSFromCISSToNOMOS20111031/src/prjCISSprjNOMOSTechnicalServicesJCD/jcdFromCISSToNOMOS.java|,
		|project://MainAccessControlTrainingBadgeOwnerService20110823/src/prjTrainingTechnicalServicesJCD/jcdFromBadgeOwnerToASSTestAdmin.java|,
		|project://MainWingsBASCISSFlightsToBAS20111108/src/prjMainprjWingsprjBASTechnicalServicesJCD/jcdCISSFlightsToBAS.java|,
		|project://OASpacesFromSpacesContractsToSkidataSGOEST20111122/src/prjOAprjSPACESTechnicalServicesJCD/jcdContracsToSkidataTranslator.java|
	];
	
list[loc] oneFileLocation = [|project://MainWingsBASCISSFlightsToBAS20111108/src/prjMainprjWingsprjBASTechnicalServicesJCD/jcdCISSFlightsToBAS.java|];

public list[EssayMetrics] calculateEssayMetrics(){
	return
		for ( loc location <- fileLocations){ // for all files, extract the java class info from the file
			append calculateMetricForLocation(location);
		}
}

// Calculates the metrics as described in the essay.
public EssayMetrics calculateMetricForLocation(loc location){
		classTree = extractClass(location);

		classes = classTree@classes;
		str packageName = getPackageName(classes);
		str className = getClassName(classes);
		
		println("Analysing class : <className>");

		int totalCC = calculateCCForClass(classTree@methodBodies);
		int moduleSize = countLOCForClass(location);
		if (moduleSize <=0) throw "Module size is not positive: <moduleSize>";
		real ccPerNLOC = totalCC/(moduleSize/Constants::PERLOCAMOUNT);
		int totalAdjustedCC = calculateAdjustedCCForClass(classTree@methodBodies);
		real adjustedCCPerNLOC = totalAdjustedCC/(moduleSize/Constants::PERLOCAMOUNT);
		//println("Location: <location> has a cyclomatic complexity of: <totalCC>");
		//println("Location: <location> has a adjusted cyclomatic complexity of: <totalAdjustedCC>");
		
		return essaymetrics(
							packageName, 
							className, 
							moduleSize,
							totalCC, 
							ccPerNLOC, 
							totalAdjustedCC, 
							adjustedCCPerNLOC);
}

// Calculates the total CC for all the AST Nodes. It assumes there is only ONE class in the
// project. This is correct for the mapping code project, but might fail with other
// projects.
private int calculateCCForClass(AstNodeRel astNodes){
	int totalCyclomaticComplexity = 0;
	
	for (tuple[Entity method,AstNode body] astNodeRel <- astNodes){ // for all AST nodes in the file calculate the total CC and aggregate it.
		methodCC = calculateCC(astNodeRel.body);
		totalCyclomaticComplexity = totalCyclomaticComplexity + methodCC;
		//println("Method <createQualifiedMethodsName(astNodeRel.method)> has a cyclomatic complexity of: <methodCC>");
	}
	
	return totalCyclomaticComplexity;
}

// Calculates the adjusted total CC for all the AST Nodes. It assumes there is only ONE class in the
// project. This is correct for the mapping code project, but might fail with other
// projects.
private int calculateAdjustedCCForClass(AstNodeRel astNodes){
	int totalAdjustedCC = 0;
	
	for (tuple[Entity method,AstNode body] astNodeRel <- astNodes){ // for all AST nodes in the file calculate the total CC and aggregate it.
		methodCC = calculateAdjustedCC(astNodeRel.body);
		totalAdjustedCC = totalAdjustedCC + methodCC;
		//println("Method <createQualifiedMethodsName(astNodeRel.method)> has an adjusted cyclomatic complexity of: <methodCC>");
	}
	
	return totalAdjustedCC;
}


// Gets the fully qualified class name for the class. Assumes one 1 class is present
private str getClassName(BindingRel classes){
	rel[loc location, Entity entity] relation = classes;
	return createQualifiedClassName(getOneFrom(relation.entity));
}

// Gets the fully qualified package name for the class. Assumes one 1 class is present
private str getPackageName(BindingRel classes){
	rel[loc location, Entity entity] relation = classes;
	return createQualifiedPackageName(getOneFrom(relation.entity));
}